package com.ljx.commandMode;

import lombok.Data;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 *
 * 命令模式
 *
 * 定义：
 * 将一个请求封装为一个对象，使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通，这样方便将命令对象进行存储、传递、调用、增加与管理
 *
 * 优点：
 * * 降低系统的耦合度。命令模式能将调用操作的对象与实现该操作的对象解耦。
 * * 增加或删除命令非常方便。采用命令模式增加与删除命令不会影响其他类，它满足“开闭原则”，对扩展比较灵活。
 * * 可以实现宏命令。命令模式可以与组合模式结合，将多个命令装配成一个组合命令，即宏命令。
 * * 方便实现 Undo 和 Redo 操作。命令模式可以与后面介绍的备忘录模式结合，实现命令的撤销与恢复。
 *
 * 缺点：
 * * 使用命令模式可能会导致某些系统有过多的具体命令类。
 * * 系统结构更加复杂。
 *
 * 使用场景：
 * * 系统需要将请求调用者和请求接收者解耦，使得调用者和接收者不直接交互。
 * * 系统需要在不同的时间指定请求、将请求排队和执行请求。
 * * 系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作
 *
 * 经典案例：
 * Runable是一个典型命令模式，Runnable担当命令的角色，Thread充当的是调用者，start方法就是其执行方法
 *
 * @作者 LJX
 * @日期 2022/12/23 16:22
 */
public class CommandTest {

    public static void main(String[] args) {

        Order o1 = new Order(1);
        o1.addFood("疙瘩汤", 2);
        o1.addFood("长寿面", 2);
        o1.addFood("饺子", 2);

        Order o2 = new Order(2);
        o2.addFood("炸鸡", 3);
        o2.addFood("汉堡", 3);
        o2.addFood("薯条", 3);

        Order o3 = new Order(3);
        o3.addFood("意面", 1);
        o3.addFood("披萨", 1);
        o3.addFood("牛排", 1);

        Cook cook = new Cook();
        OrderCommand oc1 = new OrderCommand(cook, o1);
        OrderCommand oc2 = new OrderCommand(cook, o2);
        OrderCommand oc3 = new OrderCommand(cook, o3);

        Waiter waiter = new Waiter();
        waiter.addCommand(oc1);
        waiter.addCommand(oc2);
        waiter.addCommand(oc3);

        waiter.executeCommand();

    }

}

/**
 * 命令接口 （抽象命令类角色）
 */
interface Command{
    /**
     * 执行方法
     */
    void execute();//只需要定义一个统一的执行方法
}

/**
 * 具体命令角色
 */
class OrderCommand implements Command{

    //厨师
    private Cook cook;
    //订单
    private Order order;

    public OrderCommand(Cook cook, Order order) {
        this.cook = cook;
        this.order = order;
    }

    @Override
    public void execute() {
        System.out.println("==============" + order.getTableIndex() + "号桌订单开始制作☹☹☹☹☹☹☹===============");
        //遍历菜品 逐一制作
        Map<String, Integer> map = order.getMap();
        for (String s : map.keySet()) {
            cook.makeFood(s, map.get(s));
        }
        System.out.println("==============" + order.getTableIndex() + "号桌订单制作完毕☺☺☺☺☺☺☺===============");

    }
}

/**
 * 厨师（实现者/接收者角色）
 */
class Cook{

    public void makeFood(String foodName, Integer num){
        System.out.println("做了" + num + "份，" + foodName);
    }

}

/**
 * 订单
 */
@Data
class Order{
    //桌号
    private Integer tableIndex;
    //具体餐点
    private Map<String, Integer> map = new HashMap<>();

    public Order(Integer tableIndex) {
        this.tableIndex = tableIndex;
    }

    /**
     * 添加食物
     * @param foodName
     * @param num
     */
    public void addFood(String foodName, Integer num){
        map.put(foodName, num);
    }

}

/**
 * 服务员 （调用者/请求者角色）
 */
@Data
class Waiter{

    private List<Command> commands = new ArrayList<>();

    /**
     * 添加订单
     * @param command
     */
    public void addCommand(Command command){
        commands.add(command);
    }

    /**
     * 执行订单
     */
    public void executeCommand(){
        for (Command command : commands) {
            if(null != command){
                command.execute();
            }
        }
    }

}

